/*
 * Famon (http://code.google.com/p/famon/)
 * Copyright (c) 2010 Carl Crous.
 *
 * This file is part of Famon.
 *
 * Famon is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Famon is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Famon.  If not, see <http://www.gnu.org/licenses/>.
 */

package za.org.foobar.famon.webapp;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.users.User;
import com.google.appengine.api.users.UserService;
import com.google.appengine.api.users.UserServiceFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import za.org.foobar.famon.model.UserData;
import za.org.foobar.famon.util.DB;

import java.util.*;


public class UserLogic {
    private final Log log = LogFactory.getLog(getClass());
  
    private static UserLogic instance;
    public static final String SYSTEM_USER_EMAIL = "system";
    public static final String SYSTEM_USER_NICKNAME = "system";

    public static UserLogic get() {
        return instance == null ? instance = new UserLogic() : instance;
    }

    private UserService userService = UserServiceFactory.getUserService();
    private Map<String,UserData> userCache = new HashMap<String, UserData>();
    private static ThreadLocal<Boolean> isSystemUserThreadLocal = new ThreadLocal<Boolean>();
    private UserData systemUser;

    public UserLogic() {
        log.debug("Creating UserLogic");
    }

    public UserData getCurrentUser() {
        if (isSystemUser()) {
            return getSystemUser();
        }
        if (!userService.isUserLoggedIn()) {
            return null;
        }
        User user = userService.getCurrentUser();
        if (user == null) {
            return null;
        }
        UserData userData = userCache.get(user.getUserId());
        if (userData != null) {
            Calendar twoHoursAgo = Calendar.getInstance();
            twoHoursAgo.add(Calendar.HOUR, -2);
            Date lastLogin = userData.getLastLogin();
            if (lastLogin != null && twoHoursAgo.getTime().compareTo(lastLogin) < 0) {
                return userData;
            }
        }
        // First match by the user object then look if we have the email.
        userData = DB.createDBQuery(UserData.class).addWhere("user", user).getSingleResult();
        if (userData == null) {
            userData = DB.createDBQuery(UserData.class).addWhere("email", user.getEmail())
                    .addWhere("user", null).getSingleResult();
            if (userData != null) {
                userData.setUser(user);
            }
        }
        if (userData == null) {
            userData = new UserData(user);
            DB.persist(userData);
        }
        userData.setLastLogin(new Date());
        userCache.put(user.getUserId(), userData);
        return userData;
    }

    public UserData getSystemUser() {
        if (systemUser == null) {
            systemUser = DB.createDBQuery(UserData.class)
                    .addWhere("email", SYSTEM_USER_EMAIL)
                    .getSingleResult();
        }
        return systemUser;
    }

    public boolean isSystemUser() {
        Boolean b = isSystemUserThreadLocal.get();
        return b != null && b;
    }

    public void setSystemUser(boolean useSystemUser) {
        isSystemUserThreadLocal.set(useSystemUser);
        // This will make sure the system user exists.
        if (useSystemUser && getSystemUser() == null) {
            systemUser = new UserData();
            systemUser.setEmail(SYSTEM_USER_EMAIL);
            systemUser.setNickname(SYSTEM_USER_NICKNAME);
            DB.persist(systemUser);
        }
    }

    public boolean isUserAdmin() {
        return isSystemUser() || userService.isUserLoggedIn() && userService.isUserAdmin();
    }

    public boolean isUserLoggedIn() {
        return isSystemUser() || userService.isUserLoggedIn();
    }

//    public List<UserDto> getUserDtoList() {
//        return createUserDtoList(DB.createDBQuery(UserData.class)
//            .addOrderBy("email desc").getResultList());
//    }
//
//    public List<UserDto> createUserDtoList(List<UserData> list) {
//        List<UserDto> dtoList = new ArrayList<UserDto>(list.size());
//        for (UserData data : list) {
//            dtoList.add(createUserDto(data));
//        }
//        return dtoList;
//    }
//
//    public List<UserDto> createUserDtoListFromUser(List<UserData> list) {
//        List<UserDto> dtoList = new ArrayList<UserDto>(list.size());
//        for (UserData data : list) {
//            dtoList.add(createUserDto(data));
//        }
//        return dtoList;
//    }
//
//    public List<UserDto> createUserDtoListFromApplicationUser(List<ApplicationUser> list) {
//        List<UserDto> dtoList = new ArrayList<UserDto>(list.size());
//        for (ApplicationUser data : list) {
//            dtoList.add(createUserDto(data.getUser()));
//        }
//        return dtoList;
//    }
//
//    public UserDto createUserDto(UserData user) {
//        if (user == null) return null;
//        UserDto dto = new UserDto();
//        dto.id = user.keyToString();
//        dto.email = user.getEmail();
//        dto.nickname = user.getNickname();
//        return dto;
//    }
//
//    public List<UserDto> setApplicationId(List<UserDto> dtos, Application app) {
//        String applicationId = app.keyToString();
//        for (UserDto dto : dtos) {
//            dto.applicationId = applicationId;
//        }
//        return dtos;
//    }
//
//    public UserDto setApplicationId(UserDto dto, Application app) {
//        dto.applicationId = app.keyToString();
//        return dto;
//    }
//
//    public UserDto getCurrentUserDto() {
//        return createUserDto(getCurrentUser());
//    }
//
//    public SecurityDto createSecurityDto() {
//        SecurityDto s = new SecurityDto();
//        s.isLoggedIn = userService.isUserLoggedIn();
//        s.isAdmin = s.isLoggedIn && userService.isUserAdmin();
//        if (s.isLoggedIn) {
//            s.user = getCurrentUserDto();
//        }
//        return s;
//    }

    public UserData getUserData(Key id) {
        return DB.find(UserData.class, id);
    }

    public String createLoginUrl(String destinationUrl) {
        return userService.createLoginURL(destinationUrl);
    }

    public String createLogoutUrl(String destinationUrl) {
        return userService.createLogoutURL(destinationUrl);
    }

    public UserData getUserByEmail(String email) {
        UserData user = DB.createDBQuery(UserData.class).addWhere("email", email).getSingleResult();
        if (user == null) {
            user = new UserData(email);
            DB.persist(user);
        }
        return user;
    }
}
